﻿using System;
using System.Xml;
using System.Xml.XPath;
using System.Xml.Schema;
using System.Reflection;
using InfoJetSoft.Service.Entity;
using InfoJetSoft.Service.Worker;

namespace InfoJetSoft.Service.Util
{
	class SchemaPSVI
	{
        //CID 0611 //2010.06.04 //Begin
		public static Assembly XmlAssembly = null;
        //CID 0611 //2010.06.04 //End
        private static PropertyInfo ElementDeclProperty = null;
        private static PropertyInfo IsNillableProperty = null;

        static SchemaPSVI()
        {
            //CID 0611 //2010.06.04 //Begin
            XmlAssembly = (new XmlDocument()).GetType().Assembly;
            //CID 0611 //2010.06.04 //End
            ElementDeclProperty = typeof(XmlSchemaElement).GetProperty("ElementDecl", BindingFlags.GetProperty | BindingFlags.NonPublic | BindingFlags.Instance);
            Type elementDeclType = XmlAssembly.GetType("System.Xml.Schema.SchemaElementDecl");
            //CID 0621 //2010.06.29 //Begin
            if (System.Environment.Version.Major < 4)
            {
                IsNillableProperty = elementDeclType.GetProperty("IsNillable", BindingFlags.GetProperty | BindingFlags.Public | BindingFlags.Instance);
            }
            else
            {
                IsNillableProperty = elementDeclType.GetProperty("IsNillable", BindingFlags.GetProperty | BindingFlags.NonPublic | BindingFlags.Instance);
            }
            //CID 0621 //2010.06.29 //End
        }

		//一个DatePicker，缺省如下：
		//<my:field1 xsi:nil="true" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"></my:field1>
		//只要在浏览器上打开了表单，用户提交后该元素包含文字，就删除xsi:nil属性。
		//这个处理在通过HTTP参数重新构造XML后调用。
		//不需要像InfoPath一样，当表单中只有一个DatePicker时把xmlns:xsi移动到了my:myFields上，只要保持不动就好了。
		//2008.05.08,在目前的代码中,有三个地方设计到xsi:nil的处理: FormRemoveNilTrueWorker, XFileAttachmentEncoder 和 XImageEncoder.
		//按照目前的顺序,FormRemoveNilTrueWorker先被调用.
		//CID 0451 //2009.11.08 在有PSVI支持情况下,XFileAttachmentEncoder和XImageEncoder中的处理不是必要的,因为都要在这里重新检查.
		//不过还是保留,在没有PSVI的支持情况下使用.
		public static void clearXsiNil(Form form)
		{
            //CID 0500 //2010.07.28 //Begin
            form.PSVITable.Clear();
            //CID 0500 //2010.07.28 //End

			//CID 0451 //2009.11.08 //Begin
			JetIdCache idCache = null;
			if( form.Manifest.SchemaSet != null )
			{
				//可以不Cache Id,不过去掉多余的JetId,能够稍微加快速度.
				idCache = new JetIdCache( form.XmlDoc.DocumentElement );
				generatePSVI( form );
			}
			//CID 0451 //2009.11.08 //End

			XmlElement root = form.XmlDoc.DocumentElement;
			//找到所有的元素.
			XmlNodeList elementList = DOMUtil.selectNodes(root, "//*", 0, form.XmlNsResolver);
			foreach( XmlElement element in elementList )
			{
				//CID 0451 //2009.11.09 //Begin
				if( DOMUtil.hasElementChildren( element ) )
				{
					continue;
				}
                //CID 0712 //2010.11.24 //Begin
                if (element.NamespaceURI != null && element.NamespaceURI.Equals("http://www.w3.org/1999/xhtml"))
                {
                    continue;
                }
                //CID 0712 //2010.11.24 //End
				resetXsiNil( form, element );
				//CID 0451 //2009.11.09 //End
			}
			//CID 0451 //2009.11.08 //Begin
			if( idCache != null )
			{
				idCache.reset();
			}
			//CID 0451 //2009.11.08 //End
		}

		//CID 0451 //2009.11.08 //Begin
		public static void checkXsiNil( Form form, XmlNode node )
		{
			//CID 0464 //2009.11.13 //Begin
			if( node.OwnerDocument == null )
			{
				return;
			}
			else if( node.OwnerDocument != form.XmlDoc )
			{
				return;
			}
			//CID 0464 //2009.11.13 //End
			if( node is XmlElement )
			{
				XmlElement element = node as XmlElement;
				if( !DOMUtil.hasElementChildren( element ) )
				{
					XmlAttribute jetIdAttr = element.GetAttributeNode( JetConstant.JetId );
					if( jetIdAttr != null )
					{
						element.RemoveAttributeNode( jetIdAttr );
					}
					resetXsiNil( form, element );
					if( jetIdAttr != null )
					{
						element.SetAttributeNode( jetIdAttr );
					}
				}
			}
		}
		//CID 0451 //2009.11.08 //End

		//CID 0451 //2009.11.08 //Begin
		private static void resetXsiNil( Form form, XmlElement element )
		{
			//CID 0451 //2009.11.08 //if
			if( isNillable( form, element ) )
			{
				if( element.InnerText.Trim().Length > 0 )
				{
					//如果有内容,去掉xsi:nil属性.
					element.RemoveAttribute( "nil", JetConstant.URI_XSI );
					//CID 0490 //2010.01.28 //Begin
					element.RemoveAttribute( "xsi", JetConstant.URI_XMLNS );
					//CID 0490 //2010.01.28 //End
				}
				else
				{
					//如果无内容,添加xsi:nil="true"属性.
					//CID 0490 //2010.01.28 //Begin
					XmlAttribute xsiAttr = form.XmlDoc.CreateAttribute( "xmlns", "xsi", JetConstant.URI_XMLNS );
					xsiAttr.Value = "http://www.w3.org/2001/XMLSchema-instance";
					element.SetAttributeNode( xsiAttr );
					//CID 0490 //2010.01.28 //End
					XmlAttribute nilAttr = form.XmlDoc.CreateAttribute( "xsi", "nil", JetConstant.URI_XSI );
					nilAttr.Value = "true";
					element.SetAttributeNode( nilAttr );
				}
			}
			else
			{
				//这里还没有处理的类型是unknown,如xml_value.xsn中的token.
				//输入后xmlns:xsi仍然保留,所以如果有内容remove xsi:nil,但是没有处理无内容由添加xsi:nil的情况.
				//CID 0451 //2009.11.08 //无内容时,非Nillable Element,当然也不用添加xis:nil.
				//实际上,正确的是做法是直接删除nil;但是如果是在使用NillableSchemaTypeTable的情况下可能不正确,所以用这个保险点的做法.
				if( element.InnerText.Trim().Length > 0 )
				{
					//如果有内容,去掉xsi:nil属性.
					element.RemoveAttribute( "nil", JetConstant.URI_XSI );
				}
			}
		}
		//CID 0451 //2009.11.08 //End

        //CID 0500 //2010.08.02 //Begin
        internal static string isNillabe(Form form, XmlNode node)
        {
            if (ensurePSVI(form, node))
            {
                //CID 0611 //2010.06.04 //Begin
                if (node.SchemaInfo != null)
                {
                    XmlSchemaElement schemaElement = node.SchemaInfo.SchemaElement;
                    if (schemaElement != null)
                    {
                        //CID 0500 //2010.08.23 //Begin
                        XmlSchemaElement refElement = schemaElement;
                        while (!refElement.RefName.IsEmpty)
                        {
                            refElement = form.Manifest.SchemaSet.GlobalElements[schemaElement.RefName] as XmlSchemaElement;
                            if (refElement == null)
                            {
                                return null;
                            }
                        }
                        if (refElement != null)
                        {
                            if (refElement.IsNillable)
                            {
                                return "true";
                            }
                            else
                            {
                                return "false";
                            }
                        }
                        //CID 0500 //2010.08.23 //End
                        object elementDecl = ElementDeclProperty.GetValue(schemaElement, null);
                        if (elementDecl != null)
                        {
                            bool nillable = (bool)IsNillableProperty.GetValue(elementDecl, null);
                            if (nillable)
                            {
                                return "true";
                            }
                            else
                            {
                                return "false";
                            }
                        }
                    }
                }
            }
            return null;
            //CID 0611 //2010.06.04 //End
        }
        //CID 0500 //2010.08.02 //End

		//CID 0451 //2009.11.08 //Begin
		private static bool isNillable( Form form, XmlElement element )
		{
            //CID 0500 //2010.07.29 //Begin
            ParsedPSVI parsedPSVI = ParsedPSVI.GetParsedPSVI(form, element);
            if (parsedPSVI != null)
            {
                if (!parsedPSVI.NillableProcessed)
                {
                    parsedPSVI.Nillabe = isNillabe(form, element);
                    parsedPSVI.NillableProcessed = true;
                    if (parsedPSVI.Nillabe != null)
                    {
                        return parsedPSVI.NillableBooleanValue;
                    }
                }
                else
                {
                    if (parsedPSVI.Nillabe != null)
                    {
                        return parsedPSVI.NillableBooleanValue;
                    }
                }
            }
            //CID 0500 //2010.07.29 //End
			if( form.Manifest.NillableSchemaTypeTable != null )
			{
				//如果这是一个nillable为true的date类型元素.
				//由于SchemaTypeWorker.findSchemaType()的缺陷,目前不能处理所有的SchemaType,但是对于使用InfoPath设计的form schema是没有问题的.
				SchemaType schemaType = (SchemaType)form.Manifest.NillableSchemaTypeTable[ element.Name ];
				if( schemaType != null )
				{
					return true;
				}
			}
			//这里不需要判断Manifest.NonNillableBASE64SchemaTypeTable.
			return false;
		}
		//CID 0451 //2009.11.08 //End

		//CID 0451 //2009.11.09 //Begin
		public static bool ensurePSVI(Form form, XmlNode node)
		{
			//CID 0516 //2010.03.22 //Exception
			try
			{
                if (form.Manifest.SchemaSet != null)
                {
                    //CID 0611 //2010.06.04 //Begin
                    IXmlSchemaInfo schemaInfo = node.SchemaInfo;
                    if (schemaInfo != null)
                    {
                        //CID 0838 //2011.03.23 //Begin
                        if (schemaInfo.SchemaElement != null || 
                            schemaInfo.SchemaAttribute != null ||
                            schemaInfo.SchemaType != null)
                        {
                            return true;
                        }
                        //CID 0838 //2011.03.23 //End
                    }
                    ValidationEventHandler eventHandler = new ValidationEventHandler(SchemaPSVI.validateCallBack);
                    form.XmlDoc.Validate(eventHandler, node);
                    //CID 0611 //2010.06.04 //End
                    return true;
                }
			}
			catch( Exception exception )
			{
				//记录异常后返回False,如果有异常那么就无法ensurePSVI.
                FileLog.log("Exception in ensurePSVI: " + DOMUtil.getNodePath(node));
                FileLog.log("PublishUrl: " + form.Manifest.PublishUrl);
                //FileLog.log(form.XmlDoc.OuterXml);
				FileLog.log( exception );
			}
			return false;
			//CID 0516 //2010.03.22 //End
		}
		//CID 0451 //2009.11.09 //End

		//CID 0451 //2009.11.08 //Begin
		private static void generatePSVI(Form form)
		{
			ValidationEventHandler eventHandler = new ValidationEventHandler(SchemaPSVI.validateCallBack);
            //CID 0611 //2010.06.04 //Begin
            form.XmlDoc.Schemas = form.Manifest.SchemaSet;
			form.XmlDoc.Validate(eventHandler);
			//CID 0611 //2010.06.04 //End
		}
		//CID 0451 //2009.11.08 //End

		//CID 0451 //2009.11.08 //Begin
		private static void validateCallBack( object sender, ValidationEventArgs args) 
		{
			//Do Nothing.
		}
		//CID 0451 //2009.11.08 //End

        //CID 0500 //2010.01.21 //Begin
        public static IXmlSchemaInfo getSchemaInfo(Form form, XmlNode node)
        { 
            if( !(node is XmlAttribute || node is XmlElement) )
			{
				return null;
			}
            if (form.Manifest.SchemaSet != null)
            {
                IXmlSchemaInfo schemaInfo = node.SchemaInfo;
                return schemaInfo;
            }
            return null;
        }
        //CID 0500 //2010.01.21 //End

		//CID 0500 //2010.01.21 //Begin
        public static void fillPSVI(Form form, XmlNode node, ParsedPSVI psvi)
        {
            if (ensurePSVI(form, node))
            {
                //CID 0500 //2010.08.13 //Begin
                //CID 0611 //2010.06.04 //SchemaElement
                if (node.SchemaInfo.SchemaElement != null)
                {
                    fillPSVI(form, node.SchemaInfo.SchemaElement.ElementSchemaType, psvi);
                }
                if (node.SchemaInfo.SchemaAttribute != null)
                {
                    fillPSVI(form, node.SchemaInfo.SchemaAttribute.AttributeSchemaType, psvi);
                    psvi.Nillabe = "false";
                    psvi.NillableProcessed = true;
                }
                //CID 0500 //2010.08.13 //End
            }
        }
		//CID 0500 //2010.01.21 //End

		//CID 0500 //2010.01.21 //Begin
        private static string fillPSVI(Form form, XmlSchemaType schemaType, ParsedPSVI psvi)
		{
			if( schemaType is XmlSchemaSimpleType )
			{
                fillPSVI(form, schemaType as XmlSchemaSimpleType, psvi);
			}
			else if( schemaType is XmlSchemaComplexType )
			{
                fillPSVI(form, schemaType as XmlSchemaComplexType, psvi);
			}
			return null;
		}
		//CID 0500 //2010.01.21 //End

        //CID 0500 //2010.07.27 //Begin
        private static void fillPSVI(Form form, XmlSchemaSimpleType simpleType, ParsedPSVI psvi)
        {
            if (simpleType.QualifiedName.Namespace != null && simpleType.QualifiedName.Namespace.Equals("http://www.w3.org/2001/XMLSchema"))
            {
                //SimpleType Restriction.xsn my:field3
                psvi.Type = simpleType.QualifiedName.Name;
                return;
            }
            if (simpleType.DerivedBy == XmlSchemaDerivationMethod.Restriction)
            {
                ////SimpleType Restriction.xsn my:field2
                fillPSVI(form, simpleType.BaseXmlSchemaType, psvi);
                fillFacets(form, (simpleType.Content as XmlSchemaSimpleTypeRestriction).Facets, psvi);
            }
            //Other DerivationMethod is not supported now.
        }
        //CID 0500 //2010.07.27 //End

		//CID 0500 //2010.01.21 //Begin
        private static void fillPSVI(Form form, XmlSchemaComplexType complexType, ParsedPSVI psvi)
		{
			if( complexType.ContentModel is XmlSchemaSimpleContent )
			{
				XmlSchemaSimpleContent simpleContent = complexType.ContentModel as XmlSchemaSimpleContent;
                if (simpleContent.Content is XmlSchemaSimpleContentExtension)
                {
                    XmlSchemaSimpleContentExtension extension = simpleContent.Content as XmlSchemaSimpleContentExtension;
                    XmlQualifiedName typeName = extension.BaseTypeName;
                    if (!typeName.IsEmpty)
                    {
                        if (typeName.Namespace != null && typeName.Namespace.Equals("http://www.w3.org/2001/XMLSchema"))
                        {
                            //ComplexType Restriction.xsn my:field3
                            psvi.Type = typeName.Name;
                        }
                        else
                        {
                            XmlSchemaType schemaType = form.Manifest.SchemaSet.GlobalTypes[typeName] as XmlSchemaType;
                            if (schemaType != null)
                            {
                                //ComplexType Restriction.xsn my:field2
                                fillPSVI(form, schemaType, psvi);
                            }
                        }
                    }
                }
                else if( simpleContent.Content is XmlSchemaSimpleContentRestriction)
                {
                    XmlSchemaSimpleContentRestriction restriction = simpleContent.Content as XmlSchemaSimpleContentRestriction;
                    if (restriction.BaseType != null)
                    {
                        fillPSVI(form, restriction.BaseType, psvi);
                    }
                    else
                    {
                        if (restriction.BaseTypeName.Namespace != null && restriction.BaseTypeName.Namespace.Equals("http://www.w3.org/2001/XMLSchema"))
                        {
                            psvi.Type = restriction.BaseTypeName.Name;
                        }
                        else
                        {
                            //Chapter14.xsn size
                            XmlSchemaType schemaType = form.Manifest.SchemaSet.GlobalTypes[restriction.BaseTypeName] as XmlSchemaType;
                            if (schemaType != null)
                            {
                                fillPSVI(form, schemaType, psvi);
                            }
                        }
                    }
                    fillFacets(form, restriction.Facets, psvi);
                }
			}
		}
		//CID 0500 //2010.01.21 //End

        //CID 0500 //2010.08.13 //Begin
        private static void fillFacets(Form form, XmlSchemaObjectCollection facets, ParsedPSVI psvi)
        {
            foreach (XmlSchemaFacet facet in facets)
            {
                if (facet is XmlSchemaPatternFacet)
                {
                    XmlSchemaPatternFacet patternFacet = facet as XmlSchemaPatternFacet;
                    psvi.Pattern = patternFacet.Value;
                }
                else if (facet is XmlSchemaEnumerationFacet)
                {
                    XmlSchemaEnumerationFacet enumFacet = facet as XmlSchemaEnumerationFacet;
                    psvi.Enumeration = enumFacet.Value;
                }
                else if (facet is XmlSchemaLengthFacet)
                {
                    XmlSchemaLengthFacet lengthFacet = facet as XmlSchemaLengthFacet;
                    psvi.Length = lengthFacet.Value;
                }
                else if (facet is XmlSchemaMinLengthFacet)
                {
                    XmlSchemaMinLengthFacet minLengthFacet = facet as XmlSchemaMinLengthFacet;
                    psvi.MinLength = minLengthFacet.Value;
                }
                else if (facet is XmlSchemaMaxLengthFacet)
                {
                    XmlSchemaMaxLengthFacet maxLengthFacet = facet as XmlSchemaMaxLengthFacet;
                    psvi.MaxLength = maxLengthFacet.Value;
                }
                else if (facet is XmlSchemaMinInclusiveFacet)
                {
                    XmlSchemaMinInclusiveFacet minInclusiveFacet = facet as XmlSchemaMinInclusiveFacet;
                    psvi.MinInclusive = minInclusiveFacet.Value;
                }
                else if (facet is XmlSchemaMaxInclusiveFacet)
                {
                    XmlSchemaMaxInclusiveFacet maxInclusiveFacet = facet as XmlSchemaMaxInclusiveFacet;
                    psvi.MaxInclusive = maxInclusiveFacet.Value;
                }
                else if (facet is XmlSchemaMinExclusiveFacet)
                {
                    XmlSchemaMinExclusiveFacet minExclusiveFacet = facet as XmlSchemaMinExclusiveFacet;
                    psvi.MinExclusive = minExclusiveFacet.Value;
                }
                else if (facet is XmlSchemaMaxExclusiveFacet)
                {
                    XmlSchemaMaxExclusiveFacet maxExclusiveFacet = facet as XmlSchemaMaxExclusiveFacet;
                    psvi.MaxExclusive = maxExclusiveFacet.Value;
                }
                else if (facet is XmlSchemaFractionDigitsFacet)
                {
                    XmlSchemaFractionDigitsFacet fractionDigitsFacet = facet as XmlSchemaFractionDigitsFacet;
                    psvi.FractionDigits = fractionDigitsFacet.Value;
                }
                else if (facet is XmlSchemaTotalDigitsFacet)
                {
                    XmlSchemaTotalDigitsFacet totalDigitsFacet = facet as XmlSchemaTotalDigitsFacet;
                    psvi.TotalDigits = totalDigitsFacet.Value;
                }
                else if (facet is XmlSchemaWhiteSpaceFacet)
                {
                    XmlSchemaWhiteSpaceFacet whiteSpaceFacet = facet as XmlSchemaWhiteSpaceFacet;
                    psvi.WhiteSpace = whiteSpaceFacet.Value;
                }
            }
        }
        //CID 0500 //2010.08.13 //End
	}
}